{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial - Modeling"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a basic tutorial on how to use ROSS (rotordynamics open-source software), a Python library for rotordynamic analysis. Most of this code follows object-oriented paradigm, which is represented in this \n",
    "[UML DIAGRAM](https://user-images.githubusercontent.com/32821252/50386686-131c5200-06d3-11e9-9806-f5746295be81.png).  \n",
    "\n",
    "Before starting the tutorial, it is worth noting some of ROSS’ design characteristics.\n",
    "\n",
    "First, we can divide the use of ROSS in two steps:\n",
    " - Building the model;\n",
    " - Calculating the results.\n",
    " \n",
    "We can build a model by instantiating elements such as beams (shaft), disks and bearings. These elements are all defined in classes with names such as `ShaftElement`, `BearingElement` and so on. \n",
    "\n",
    "After instantiating some elements, we can then use these to build a rotor.\n",
    "\n",
    "This tutorial is about building your **rotor model**. First, you will learn how to create and assign **materials**, how to instantiate the **elements** which composes the rotor and how to convert **units** in ROSS with [pint](https://pint.readthedocs.io/en/stable/) library. This means that every time we call a function, we can use pint.Quantity as an argument for values that have units. If we give a float to the function ROSS will consider SI units as default.\n",
    "\n",
    "In the following topics, we will discuss the most relevant classes for a quick start on how to use ROSS."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Table of Contents\n",
    "\n",
    "- 1. [Material Class](#section1)\n",
    "    - 1.1. [Creating a material](#section1.1)\n",
    "    - 1.2. [Saving materials](#section1.2)\n",
    "    - 1.3. [Available materials](#section1.3)\n",
    "    - 1.4. [Loading materials](#section1.4)\n",
    "\n",
    "- 2. [ShaftElement Class](#section2)\n",
    "    - 2.1. [Creating shaft elements](#section2.1)\n",
    "        - 2.1.1. [Cylindrical shaft element](#section2.1.1)\n",
    "        - 2.1.2. [Conical shaft element](#section2.1.2)\n",
    "        - 2.1.3. [List of elements - identical properties](#section2.1.3)\n",
    "        - 2.1.4. [List of elements - different properties](#section2.1.4)\n",
    "    - 2.2. [Creating shaft elements via Excel](#section2.2)\n",
    "    \n",
    "- 3. [DiskElement Class](#section3)\n",
    "    - 3.1. [Creating disk elements from inertia properties](#section3.1)\n",
    "        - 3.1.1. [Creating a single disk element](#section3.1.1)\n",
    "        - 3.1.2. [Creating a list of disk element](#section3.1.2)\n",
    "    - 3.2. [Creating disk elements from geometrical properties](#section3.2)\n",
    "        - 3.2.1. [Creating a single disk element](#section3.2.1)\n",
    "        - 3.2.2. [Creating a list of disk element](#section3.2.2)\n",
    "    - 3.3. [Creating disk elements via Excel](#section3.3)\n",
    "\n",
    "- 4. [Bearing and Seal Classes](#section4)\n",
    "    - 4.1. [BearingElement Class](#section4.1)\n",
    "        - 4.1.1. [Bearing with constant coefficients](#section4.1.1)\n",
    "        - 4.1.2. [Bearing with varying coefficients](#section4.1.2)\n",
    "        - 4.1.3. [Inserting bearing elements in series](#section4.1.3)\n",
    "        - 4.1.4. [Visualizing coefficients graphically](#section4.1.4)\n",
    "    - 4.2. [SealElement Class](#section4.2)\n",
    "    - 4.3. [BallBearingElement Class](#section4.3)\n",
    "    - 4.4. [RollerBearingElement Class](#section4.4)\n",
    "    - 4.5. [MagneticElement Class](#section4.5)\n",
    "        - 4.5.1. [MagneticBearing from electromagnetic parameters](#section4.5.1)\n",
    "    - 4.6. [Creating bearing elements via Excel ](#section4.6)\n",
    "\n",
    "- 5. [PointMass Class](#section5)\n",
    "\n",
    "- 6. [Rotor Class](#section6)\n",
    "    - 6.1. [Creating a rotor model](#section6.1)\n",
    "    - 6.2. [Creating a rotor from sections](#section6.2)\n",
    "    - 6.3. [Visualizing the rotor model](#section6.3)\n",
    "    - 6.4. [Saving a rotor model](#section6.4)\n",
    "    - 6.5. [Loading a rotor model](#section6.5)\n",
    "\n",
    "- 7. [ROSS Units System](#section7)\n",
    "    - 7.1 [Inserting units](#section7.1)\n",
    "    - 7.2 [Manipulating units for plotting](#section7.2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from pathlib import Path\n",
    "import ross as rs\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 1: Material Class <a id='section1'></a>\n",
    "\n",
    "There is a class called Material to hold material's properties. Materials are applied to shaft and disk elements.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1 Creating a material <a id='section1.1'></a>\n",
    "\n",
    "To instantiate a Material class, you only need to give 2 out of \n",
    "the following parameters: `E` (Young's Moduluds), `G_s` (Shear \n",
    "Modulus) ,`Poisson` (Poisson Coefficient), and the material \n",
    "density `rho`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Steel\n",
      "-----------------------------------\n",
      "Density         (kg/m**3): 7810.0\n",
      "Young`s modulus (N/m**2):  2.11e+11\n",
      "Shear modulus   (N/m**2):  8.12e+10\n",
      "Poisson coefficient     :  0.29926108\n",
      "====================================\n",
      "Young's Modulus: 211000000000.0\n",
      "Shear Modulus:    81200000000.0\n"
     ]
    }
   ],
   "source": [
    "# from E and G_s\n",
    "steel = rs.Material(name=\"Steel\", rho=7810, E=211e9, G_s=81.2e9)\n",
    "# from E and Poisson\n",
    "steel2 = rs.Material(name=\"Steel\", rho=7810, E=211e9, Poisson=0.3)\n",
    "# from G_s and Poisson\n",
    "steel3 = rs.Material(name=\"Steel\", rho=7810, G_s=81.2e9, Poisson=0.3)\n",
    "\n",
    "print(steel)\n",
    "\n",
    "# returning attributes\n",
    "print(\"=\"*36)\n",
    "print(f\"Young's Modulus: {steel.E}\")\n",
    "print(f\"Shear Modulus:    {steel.G_s}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**: Adding 3 arguments to the Material class raises an error."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2 Saving materials <a id='section1.2'></a>\n",
    "\n",
    "To save an already instantiated Material object, you need to use the following method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "steel.save_material()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3 Available materials <a id='section1.3'></a>\n",
    "\n",
    "Saved Materials are stored in a **.toml file**, which can be read as .txt. The file is placed on ROSS root file with name `available_materials.toml`.\n",
    "\n",
    "It's possible to access the Material data from the file. With the file opened, you can:\n",
    " - modify the properties directly;\n",
    " - create new materials;\n",
    "\n",
    "It's important to **keep the file structure** to ensure the correct functioning of the class.\n",
    "\n",
    "```\n",
    "[Materials.Steel]\n",
    "name = \"Steel\"\n",
    "rho = 7810\n",
    "E = 211000000000.0\n",
    "Poisson = 0.2992610837438423\n",
    "G_s = 81200000000.0\n",
    "color = \"#525252\"\n",
    "```\n",
    "\n",
    "**Do not change the dictionary keys and the order they're built**.\n",
    "\n",
    "To check what materials are available, use the command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Steel']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rs.Material.available_materials()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 Loading materials <a id='section1.4'></a>\n",
    "\n",
    "After checking the available materials, you should use the `Material.use_material('name')` method with the **name of the material** as a parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "steel5 = rs.Material.load_material('Steel')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 2: ShaftElement Class <a id='section2'></a>\n",
    "\n",
    "`ShaftElement` allows you to create cylindrical and conical shaft elements. It means you can set differents outer and inner diameters for each element node.\n",
    "\n",
    "There are some ways in which you can choose the parameters to model this element:\n",
    "\n",
    "- Euler–Bernoulli beam Theory (`rotary_inertia=False, shear_effects=False`)\n",
    "- Timoshenko beam Theory (`rotary_inertia=True, shear_effects=True` - used as default)\n",
    "\n",
    "The matrices (mass, stiffness, damping and gyroscopic) will be defined considering the following local coordinate vector:\n",
    "\n",
    "$[x_0, y_0, \\alpha_0, \\beta_0, x_1, y_1, \\alpha_1, \\beta_1]^T$\n",
    "Where \n",
    "$\\alpha_0$ and $\\alpha_1$ are the bending on the yz plane\n",
    "$\\beta_0$ and $\\beta_1$ are the bending on the xz plane.\n",
    "\n",
    "\n",
    "This element represents the rotor's shaft, all the other elements are correlated with this one."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.1 Creating shaft elements <a id='section2.1'></a>\n",
    "\n",
    "The next examples present different ways of how to create a ShaftElement object, from a single element to a list of several shaft elements with different properties.\n",
    "\n",
    "When creating shaft elements, you don't necessarily need to input a specific node. If `n=None`, the `Rotor` class will assign a value to the element when building a rotor model (*see section 6*).\n",
    "\n",
    "You can also pass the same `n` value to several shaft elements in the same rotor model. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.1 Cylindrical shaft element <a id='section2.1.1'></a>\n",
    "\n",
    "As it's been seen, a shaft element has 4 parameters for diameters. To simplify that, when creating a cylindrical element, you only need to give 2 of them: `idl` and `odl`. So the other 2 (`idr` and `odr`) get the same values.\n",
    "\n",
    "**Note**: you can give all the 4 parameters, as long they match each other."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Element Number:             None\n",
      "Element Lenght   (m):       0.25\n",
      "Left Int. Diam.  (m):        0.0\n",
      "Left Out. Diam.  (m):       0.05\n",
      "Right Int. Diam. (m):        0.0\n",
      "Right Out. Diam. (m):       0.05\n",
      "-----------------------------------\n",
      "Steel\n",
      "-----------------------------------\n",
      "Density         (kg/m**3): 7810.0\n",
      "Young`s modulus (N/m**2):  2.11e+11\n",
      "Shear modulus   (N/m**2):  8.12e+10\n",
      "Poisson coefficient     :  0.29926108\n"
     ]
    }
   ],
   "source": [
    "# Cylindrical shaft element\n",
    "L = 0.25\n",
    "i_d = 0\n",
    "o_d = 0.05\n",
    "cy_elem = rs.ShaftElement(\n",
    "    L=L,\n",
    "    idl=i_d,\n",
    "    odl=o_d,\n",
    "    material=steel,\n",
    "    shear_effects=True,\n",
    "    rotary_inertia=True,\n",
    "    gyroscopic=True,\n",
    ")\n",
    "\n",
    "print(cy_elem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.2 Conical shaft element\n",
    "<a id='section2.1.2'></a>\n",
    "\n",
    "To create a conical shaft elements, you must give all the 4 diamater parameters, and `idl != idr` and/or `odl != odr`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Element Number:             None\n",
      "Element Lenght   (m):       0.25\n",
      "Left Int. Diam.  (m):        0.0\n",
      "Left Out. Diam.  (m):       0.05\n",
      "Right Int. Diam. (m):        0.0\n",
      "Right Out. Diam. (m):       0.07\n",
      "-----------------------------------\n",
      "Steel\n",
      "-----------------------------------\n",
      "Density         (kg/m**3): 7810.0\n",
      "Young`s modulus (N/m**2):  2.11e+11\n",
      "Shear modulus   (N/m**2):  8.12e+10\n",
      "Poisson coefficient     :  0.29926108\n"
     ]
    }
   ],
   "source": [
    "# Conical shaft element\n",
    "L = 0.25\n",
    "idl = 0\n",
    "idr = 0\n",
    "odl = 0.05\n",
    "odr = 0.07\n",
    "co_elem = rs.ShaftElement(\n",
    "    L=L,\n",
    "    idl=idl,\n",
    "    idr=idr,\n",
    "    odl=odl,\n",
    "    odr=odr,\n",
    "    material=steel,\n",
    "    shear_effects=True,\n",
    "    rotary_inertia=True,\n",
    "    gyroscopic=True,\n",
    ")\n",
    "print(co_elem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Returning element matrices\n",
    "\n",
    "Use one of this methods to return the matrices:\n",
    "- `.M()`: returns the mass matrix\n",
    "- `.K(frequency)`: returns the stiffness matrix\n",
    "- `.C(frequency)`: returns the damping matrix\n",
    "- `.G()`: returns de gyroscopic matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mass matrix\n",
    "# cy_elem.M()\n",
    "\n",
    "# Stiffness matrix\n",
    "# frequency = 0\n",
    "# cy_elem.K(frequency)\n",
    "\n",
    "# Damping matrix\n",
    "# frequency = 0\n",
    "# cy_elem.C(frequency)\n",
    "\n",
    "# Gyroscopic matrix\n",
    "# cy_elem.G()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.3 List of elements - identical properties <a id='section2.1.3'></a>\n",
    "\n",
    "Now we leanrt how to create elements, let's automate the process of creating multiple elements with identical properties.\n",
    "\n",
    "In this example, we want 6 shaft elements with identical properties. This process can be done using a `for` loop or a list comprehension."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.25, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None)]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Creating a list of shaft elements\n",
    "L = 0.25\n",
    "i_d = 0\n",
    "o_d = 0.05\n",
    "N = 6\n",
    "shaft_elements = [\n",
    "    rs.ShaftElement(\n",
    "        L=L,\n",
    "        idl=i_d,\n",
    "        odl=o_d,\n",
    "        material=steel,\n",
    "        shear_effects=True,\n",
    "        rotary_inertia=True,\n",
    "        gyroscopic=True,\n",
    "    )\n",
    "    for _ in range(N)\n",
    "]\n",
    "shaft_elements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 2.1.4 List of elements - different properties <a id='section2.1.4'></a>\n",
    "\n",
    "Now we leanrt how to create elements, let's automate the process of creating multiple elements with identical properties.\n",
    "\n",
    "In this example, we want 6 shaft elements which properties may not be the same. This process can be done using a `for` loop or a list comprehension, coupled with Python's `zip()` method.\n",
    "\n",
    "We create lists for each property, where each term refers to a single element:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ShaftElement(L=0.2, idl=0.01, idr=0.01, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.1, idl=0.0, idr=0.0, odl=0.06,  odr=0.06, material='Steel', n=None),\n",
       " ShaftElement(L=0.1, idl=0.0, idr=0.0, odl=0.06,  odr=0.06, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.01, idr=0.01, odl=0.05,  odr=0.05, material='Steel', n=None)]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.1:\n",
    "# Using zip() method\n",
    "L =   [0.20, 0.20, 0.10, 0.10, 0.20, 0.20]\n",
    "i_d = [0.01,    0,    0,    0,    0, 0.01]\n",
    "o_d = [0.05, 0.05, 0.06, 0.06, 0.05, 0.05]\n",
    "shaft_elements = [\n",
    "    rs.ShaftElement(\n",
    "        L=l,\n",
    "        idl=idl,\n",
    "        odl=odl,\n",
    "        material=steel,\n",
    "        shear_effects=True,\n",
    "        rotary_inertia=True,\n",
    "        gyroscopic=True,\n",
    "    )\n",
    "    for l, idl, odl in zip(L, i_d, o_d)\n",
    "]\n",
    "shaft_elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ShaftElement(L=0.2, idl=0.01, idr=0.01, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.1, idl=0.0, idr=0.0, odl=0.06,  odr=0.06, material='Steel', n=None),\n",
       " ShaftElement(L=0.1, idl=0.0, idr=0.0, odl=0.06,  odr=0.06, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.0, idr=0.0, odl=0.05,  odr=0.05, material='Steel', n=None),\n",
       " ShaftElement(L=0.2, idl=0.01, idr=0.01, odl=0.05,  odr=0.05, material='Steel', n=None)]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.2:\n",
    "# Using list index\n",
    "L =   [0.20, 0.20, 0.10, 0.10, 0.20, 0.20]\n",
    "i_d = [0.01,    0,    0,    0,    0, 0.01]\n",
    "o_d = [0.05, 0.05, 0.06, 0.06, 0.05, 0.05]\n",
    "N = len(L)\n",
    "shaft_elements = [\n",
    "    rs.ShaftElement(\n",
    "        L=L[i],\n",
    "        idl=i_d[i],\n",
    "        odl=o_d[i],\n",
    "        material=steel,\n",
    "        shear_effects=True,\n",
    "        rotary_inertia=True,\n",
    "        gyroscopic=True,\n",
    "    )\n",
    "    for i in range(N)\n",
    "]\n",
    "shaft_elements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 Creating shaft elements via Excel <a id='section2.2'></a>\n",
    "\n",
    "There is an option for creating a list of shaft elements via an Excel file. The classmethod `.from_table()` reads an Excel file created and converts it to a list of shaft elements.\n",
    "\n",
    "A header with the names of the columns is required. These names should match the names expected by the routine (usually the names of the parameters, but also similar ones). The program will read every row bellow the header until they end or it reaches a NaN, which means if the code reaches to an empty line, it stops iterating.\n",
    "\n",
    "An example of Excel content can be found at ROSS GitHub repository at *ross/tests/data/shaft_si.xls*, spreadsheet \"Model\".\n",
    "\n",
    "You can load it using the following code."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "shaft_file = Path(\"shaft_si.xls\")\n",
    "shaft = rs.ShaftElement.from_table(\n",
    "    file=shaft_file, sheet_type=\"Model\", sheet_name=\"Model\"\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 3: DiskElement Class <a id='section3'></a>\n",
    "\n",
    "The class `DiskElement` allows you to create disk elements, representing rotor equipments which can be considered only to add mass and inertia to the system, disregarding the stiffness.\n",
    "\n",
    "ROSS offers 3 (three) ways to create a disk element:\n",
    "1. Inputing mass and inertia data\n",
    "2. Inputing geometrical and material data\n",
    "3. From Excel table"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.1 Creating disk elements from inertia properties <a id='section3.1'></a>\n",
    "\n",
    "If you have access to the mass and inertia properties of a equipment, you can input the data directly to the element.\n",
    "\n",
    "Disk elements are useful to represent equipments which mass and inertia are significant, but the stiffness can be neglected."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.1.1 Creating a single disk element <a id='section3.1.1'></a>\n",
    "\n",
    "This example below shows how to instantiate a disk element according to the mass and inertia properties:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DiskElement(Id=0.329, Ip=0.178, m=32.58, color='Firebrick', n=0, scale_factor=1.0, tag='Disk')"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "disk = rs.DiskElement(\n",
    "    n=0,\n",
    "    m=32.58,\n",
    "    Ip=0.178,\n",
    "    Id=0.329,\n",
    "    tag=\"Disk\"\n",
    ")\n",
    "disk"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.1.2 Creating a list of disk element <a id='section3.1.2'></a>\n",
    "\n",
    "This example below shows how to create a list of disk element according to the mass and inertia properties. The logic is the same applied to shaft elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[DiskElement(Id=0.17809, Ip=0.32956, m=32.6, color='Firebrick', n=2, scale_factor=1.0, tag=None),\n",
       " DiskElement(Id=0.17809, Ip=0.38373, m=35.8, color='Firebrick', n=4, scale_factor=1.0, tag=None)]"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.1:\n",
    "# Using zip() method\n",
    "n_list = [2, 4]\n",
    "m_list = [32.6, 35.8]\n",
    "Id_list = [0.17808928, 0.17808928]\n",
    "Ip_list = [0.32956362, 0.38372842]\n",
    "disk_elements = [\n",
    "    rs.DiskElement(\n",
    "        n=n,\n",
    "        m=m,\n",
    "        Id=Id,\n",
    "        Ip=Ip,\n",
    "    )\n",
    "    for n, m, Id, Ip in zip(n_list, m_list, Id_list, Ip_list)\n",
    "]\n",
    "disk_elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[DiskElement(Id=0.17809, Ip=0.32956, m=32.6, color='Firebrick', n=2, scale_factor=1.0, tag=None),\n",
       " DiskElement(Id=0.17809, Ip=0.38373, m=35.8, color='Firebrick', n=4, scale_factor=1.0, tag=None)]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.2:\n",
    "# Using list index\n",
    "n_list = [2, 4]\n",
    "m_list = [32.6, 35.8]\n",
    "Id_list = [0.17808928, 0.17808928]\n",
    "Ip_list = [0.32956362, 0.38372842]\n",
    "N = len(n_list)\n",
    "disk_elements = [\n",
    "    rs.DiskElement(\n",
    "        n=n_list[i],\n",
    "        m=m_list[i],\n",
    "        Id=Id_list[i],\n",
    "        Ip=Ip_list[i],\n",
    "    )\n",
    "    for i in range(N)\n",
    "]\n",
    "disk_elements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.2 Creating disk elements from geometrical properties <a id='section3.2'></a>\n",
    "\n",
    "Besides the instantiation previously explained, there is a way to instantiate a DiskElement with only geometrical parameters (an approximation for cylindrical disks) and the disk’s material, as we can see in the following code. In this case, there's a class method (`rs.DiskElement.from_geometry()`) which you can use.\n",
    "\n",
    "ROSS will take geometrical parameters (outer and inner diameters, and width) and convert them into mass and inertia data. Once again, considering the disk as a cylinder."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.2.1 Creating a single disk element <a id='section3.2.1'></a>\n",
    "\n",
    "This example below shows how to instantiate a disk element according to the geometrical and material properties:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tag:                      None\n",
      "Node:                     4\n",
      "Mass           (kg):      32.59\n",
      "Diam. inertia  (kg*m**2): 0.17809\n",
      "Polar. inertia (kg*m**2): 0.32956\n",
      "============================================================================\n",
      "Disk mass:              32.58972765304033\n",
      "Disk polar inertia:     0.32956362089137037\n",
      "Disk diametral inertia: 0.17808928257067666\n"
     ]
    }
   ],
   "source": [
    "disk1 = rs.DiskElement.from_geometry(\n",
    "    n=4,\n",
    "    material=steel,\n",
    "    width=0.07,\n",
    "    i_d=0.05,\n",
    "    o_d=0.28\n",
    ")\n",
    "print(disk1)\n",
    "\n",
    "print(\"=\"*76)\n",
    "print(f\"Disk mass:              {disk1.m}\")\n",
    "print(f\"Disk polar inertia:     {disk1.Ip}\")\n",
    "print(f\"Disk diametral inertia: {disk1.Id}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 3.2.2 Creating a list of disk element <a id='section3.2.2'></a>\n",
    "\n",
    "This example below shows how to create a list of disk element according to the geometrical and material properties. The logic is the same applied to shaft elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[DiskElement(Id=3.6408, Ip=0.26836, m=85.875, color='Firebrick', n=2, scale_factor=1.0, tag=None),\n",
       " DiskElement(Id=5.5224, Ip=0.56007, m=128.38, color='Firebrick', n=4, scale_factor=1.0, tag=None)]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.1:\n",
    "# Using zip() method\n",
    "n_list = [2, 4]\n",
    "width_list = [0.7, 0.7]\n",
    "i_d_list = [0.05, 0.05]\n",
    "o_d_list = [0.15, 0.18]\n",
    "disk_elements = [\n",
    "    rs.DiskElement.from_geometry(\n",
    "        n=n,\n",
    "        material=steel,\n",
    "        width=width,\n",
    "        i_d=i_d,\n",
    "        o_d=o_d,\n",
    "    )\n",
    "    for n, width, i_d, o_d in zip(n_list, width_list, i_d_list, o_d_list)\n",
    "]\n",
    "disk_elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[DiskElement(Id=3.6408, Ip=0.26836, m=85.875, color='Firebrick', n=2, scale_factor=1.0, tag=None),\n",
       " DiskElement(Id=5.5224, Ip=0.56007, m=128.38, color='Firebrick', n=4, scale_factor=1.0, tag=None)]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# OPTION No.2:\n",
    "# Using list index\n",
    "n_list = [2, 4]\n",
    "width_list = [0.7, 0.7]\n",
    "i_d_list = [0.05, 0.05]\n",
    "o_d_list = [0.15, 0.18]\n",
    "N = len(n_list)\n",
    "disk_elements = [\n",
    "    rs.DiskElement.from_geometry(\n",
    "        n=n_list[i],\n",
    "        material=steel,\n",
    "        width=width_list[i],\n",
    "        i_d=i_d_list[i],\n",
    "        o_d=o_d_list[i],\n",
    "    )\n",
    "    for i in range(N)\n",
    "]\n",
    "disk_elements"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.3 Creating disk elements via Excel <a id='section3.3'></a>\n",
    "\n",
    "The third option for creating disk elements is via an Excel file. The classmethod `.from_table()` reads an Excel file created and converts it to a list of disk elements. This method accepts **only mass and inertia** inputs.\n",
    "\n",
    "A header with the names of the columns is required. These names should match the names expected by the routine (usually the names of the parameters, but also similar ones). The program will read every row bellow the header until they end or it reaches a NaN, which means if the code reaches to an empty line, it stops iterating.\n",
    "\n",
    "You can take advantage of the excel file used to assemble shaft elements, to assemble disk elements, just add a new spreadsheet to your Excel file and specify the correct `sheet_name`.\n",
    "\n",
    "An example of Excel content can be found at diretory *ross/tests/data/shaft_si.xls*, spreadsheet \"More\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'shaft_si.xls'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_29540\\3354428799.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[0mfile_path\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPath\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"shaft_si.xls\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mlist_of_disks\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mrs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mDiskElement\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfrom_table\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mfile_path\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msheet_name\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m\"More\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      3\u001b[0m \u001b[0mlist_of_disks\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\ross\\disk_element.py\u001b[0m in \u001b[0;36mfrom_table\u001b[1;34m(cls, file, sheet_name, tag, scale_factor, color)\u001b[0m\n\u001b[0;32m    480\u001b[0m         \u001b[0mDiskElement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mId\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mIp\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m0.0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mm\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m15.12\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcolor\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;34m'Firebrick'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mn\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m3\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mscale_factor\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mtag\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    481\u001b[0m         \"\"\"\n\u001b[1;32m--> 482\u001b[1;33m         \u001b[0mparameters\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mread_table_file\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"disk\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msheet_name\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0msheet_name\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    483\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mtag\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    484\u001b[0m             \u001b[0mtag\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mparameters\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;34m\"n\"\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\ross\\utils.py\u001b[0m in \u001b[0;36mread_table_file\u001b[1;34m(file, element, sheet_name, n, sheet_type)\u001b[0m\n\u001b[0;32m     44\u001b[0m     {'L': [0.03...\n\u001b[0;32m     45\u001b[0m     \"\"\"\n\u001b[1;32m---> 46\u001b[1;33m     \u001b[0mdf\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mread_excel\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mheader\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msheet_name\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0msheet_name\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m     47\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m     48\u001b[0m     \u001b[1;31m# Assign specific values to variables\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\pandas\\util\\_decorators.py\u001b[0m in \u001b[0;36mwrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    309\u001b[0m                     \u001b[0mstacklevel\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstacklevel\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    310\u001b[0m                 )\n\u001b[1;32m--> 311\u001b[1;33m             \u001b[1;32mreturn\u001b[0m \u001b[0mfunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m*\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    312\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    313\u001b[0m         \u001b[1;32mreturn\u001b[0m \u001b[0mwrapper\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\pandas\\io\\excel\\_base.py\u001b[0m in \u001b[0;36mread_excel\u001b[1;34m(io, sheet_name, header, names, index_col, usecols, squeeze, dtype, engine, converters, true_values, false_values, skiprows, nrows, na_values, keep_default_na, na_filter, verbose, parse_dates, date_parser, thousands, comment, skipfooter, convert_float, mangle_dupe_cols, storage_options)\u001b[0m\n\u001b[0;32m    362\u001b[0m     \u001b[1;32mif\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mio\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mExcelFile\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    363\u001b[0m         \u001b[0mshould_close\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;32mTrue\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 364\u001b[1;33m         \u001b[0mio\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mExcelFile\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mio\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstorage_options\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstorage_options\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mengine\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mengine\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    365\u001b[0m     \u001b[1;32melif\u001b[0m \u001b[0mengine\u001b[0m \u001b[1;32mand\u001b[0m \u001b[0mengine\u001b[0m \u001b[1;33m!=\u001b[0m \u001b[0mio\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    366\u001b[0m         raise ValueError(\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\pandas\\io\\excel\\_base.py\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, path_or_buffer, engine, storage_options)\u001b[0m\n\u001b[0;32m   1190\u001b[0m             \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1191\u001b[0m                 ext = inspect_excel_format(\n\u001b[1;32m-> 1192\u001b[1;33m                     \u001b[0mcontent_or_path\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mpath_or_buffer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstorage_options\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstorage_options\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1193\u001b[0m                 )\n\u001b[0;32m   1194\u001b[0m                 \u001b[1;32mif\u001b[0m \u001b[0mext\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\pandas\\io\\excel\\_base.py\u001b[0m in \u001b[0;36minspect_excel_format\u001b[1;34m(content_or_path, storage_options)\u001b[0m\n\u001b[0;32m   1069\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m   1070\u001b[0m     with get_handle(\n\u001b[1;32m-> 1071\u001b[1;33m         \u001b[0mcontent_or_path\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"rb\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstorage_options\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mstorage_options\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mis_text\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mFalse\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m   1072\u001b[0m     ) as handle:\n\u001b[0;32m   1073\u001b[0m         \u001b[0mstream\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mhandle\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mhandle\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;32mD:\\miniconda3\\envs\\ross\\lib\\site-packages\\pandas\\io\\common.py\u001b[0m in \u001b[0;36mget_handle\u001b[1;34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[0m\n\u001b[0;32m    709\u001b[0m         \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    710\u001b[0m             \u001b[1;31m# Binary mode\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 711\u001b[1;33m             \u001b[0mhandle\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mopen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mhandle\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mioargs\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m    712\u001b[0m         \u001b[0mhandles\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mhandle\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m    713\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'shaft_si.xls'"
     ]
    }
   ],
   "source": [
    "file_path = Path(\"shaft_si.xls\")\n",
    "list_of_disks = rs.DiskElement.from_table(file=file_path, sheet_name=\"More\")\n",
    "list_of_disks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 4: Bearing and Seal Classes <a id='section4'></a>\n",
    "\n",
    "ROSS has a serie of classe to represent element that adds stiffness and / or damping to a rotor system.\n",
    "They're suitable to represent mainly bearings, supports and seals. Each one aims to represent some types of bearing and seal.\n",
    "\n",
    "All the class will return four stiffness coefficients ($k_{xx}$, $k_{xy}$, $k_{yx}$, $k_{yy}$) and four damping coefficients ($c_{xx}$, $c_{xy}$, $c_{yx}$, $c_{yy}$), which will be used to assemble the stiffness and damping matrices. \n",
    "\n",
    "The main difference between these classes are the arguments the user must input to create the element.\n",
    "\n",
    "Available bearing classes and class methods:\n",
    "\n",
    "- 1. `BearingElement`: represents a general (journal) bearing element.\n",
    "- 2. `SealElement`: represents a general seal element.\n",
    "- 3. `BallBearingElement`: A bearing element for ball bearings\n",
    "- 4. `RollerBearingElement`: A bearing element for roller bearings.\n",
    "- 5. `MagneticBearingElement`: A bearing element for magnetic bearings.\n",
    "    - 5.1. `param_to_coef`: A bearing element for magnetic bearings from electromagnetic parameters\n",
    "\n",
    "The classes from item 2 to 5 inherits from `BearingElement` class. It means, you can use the same methods and commands, set up to `BearingElement`, in the other classes."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.1 BearingElement Class <a id='section4.1'></a>\n",
    "\n",
    "This class will create a bearing element. Bearings are elements that only add stiffness and damping properties to the rotor system. These parameters are defined by 8 dynamics coefficients (4 stiffness coefficients and 4 damping coefficients).\n",
    "\n",
    "Parameters can be a constant value or speed dependent. For speed dependent parameters, each argument should be passed as an array and the correspondent speed values should also be\n",
    "passed as an array. Values for each parameter will be interpolated for the speed.\n",
    "\n",
    "Bearing elements are single node elements and linked to \"ground\", but it's possible to create a new node with `n_link` argument to introduce a link with other elements. Useful to add bearings in series or co-axial rotors."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.1.1 Bearing with constant coefficients <a id='section4.1.1'></a>\n",
    "\n",
    "Bearings can have a constant value for each coefficient. In this case, it's **not necessary** to give a value to `frequency` argument.\n",
    "\n",
    "The next example shows how to instantiate a **single bearing with constant coefficients**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stfx = 1e6\n",
    "stfy = 0.8e6\n",
    "bearing1 = rs.BearingElement(n=0, kxx=stfx, kyy=stfy, cxx=1e3)\n",
    "print(bearing1)\n",
    "\n",
    "print(\"=\"*55)\n",
    "print(f\"Kxx coefficient: {bearing1.kxx}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.1.2 Bearing with varying coefficients <a id='section4.1.2'></a>\n",
    "\n",
    "The coefficients could be an array with different values for different rotation speeds, in that case you only have to give a parameter 'frequency' which is a array with the same size as the coefficients array.  \n",
    "\n",
    "The next example shows how to instantiate a **single bearing with speed dependent parameters**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "bearing2 = rs.BearingElement(\n",
    "    n=0, \n",
    "    kxx=np.array([0.5e6, 1.0e6, 2.5e6]),\n",
    "    kyy=np.array([1.5e6, 2.0e6, 3.5e6]),\n",
    "    cxx=np.array([0.5e3, 1.0e3, 1.5e3]),\n",
    "    frequency=np.array([0, 1000, 2000]),\n",
    ")\n",
    "\n",
    "print(bearing2)\n",
    "print(\"=\"*79)\n",
    "print(f\"Kxx coefficient: {bearing2.kxx}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the size of coefficient and frequency arrays do not match, an `ValueError` is raised\n",
    "\n",
    "The next example shows the instantiate of a **bearing with odd parameters**:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```python\n",
    "bearing_odd = rs.BearingElement( # odd dimensions\n",
    "    n=0, \n",
    "    kxx=np.array([0.5e6, 1.0e6, 2.5e6]),\n",
    "    kyy=np.array([1.5e6, 2.0e6, 3.5e6]),\n",
    "    cxx=np.array([0.5e3, 1.0e3, 1.5e3]),\n",
    "    frequency=np.array([0, 1000, 2000, 3000])\n",
    ")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.1.3 Inserting bearing elements in series <a id='section4.1.3'></a>\n",
    "\n",
    "Bearing and seal elements are 1-node element, which means the element attaches to a given node from the rotor shaft and it's connect to the \"ground\". However, there's an option to couple multiple elements in series, using the `n_link` argument. This is very useful to simulate structures which support the machine, for example.\n",
    "\n",
    "`n_link` opens a new node to the rotor system, or it can be associated to another rotor node (useful in co-axial rotor models). Then, the new BearingElement node, is set equal to the `n_link` from the previous element."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stfx = 1e6\n",
    "stfy = 0.8e6\n",
    "bearing3 = rs.BearingElement(n=0, kxx=stfx, kyy=stfy, cxx=1e3, n_link=1, tag=\"journal_bearing\")\n",
    "bearing4 = rs.BearingElement(n=1, kxx=1e7, kyy=1e9, cxx=10, tag=\"support\")\n",
    "print(bearing3)\n",
    "print(bearing4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 4.1.4 Visualizing coefficients graphically <a id='section4.1.3'></a>\n",
    "\n",
    "If you want to visualize how the coefficients varies with speed, you can select a specific coefficient and use the `.plot()` method.\n",
    "\n",
    "Let's return to the example done in **4.1.2** and check how $k_{yy}$ and $c_{yy}$ varies. You can check for all the 8 dynamic coefficients as you like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bearing2.plot('kyy')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bearing2.plot(['kxx', 'kyy'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bearing2.plot('cyy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.2 SealElement Class <a id='section4.2'></a>\n",
    "\n",
    "`SealElement` class method have the exactly same arguments than `BearingElement`. The differences are found in some considerations when assembbling a full rotor model. For example, a SealElement won't generate reaction forces in a static analysis. So, even they are very similar when built, they have different roles in the model.\n",
    "\n",
    "Let's see an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "stfx = 1e6\n",
    "stfy = 0.8e6\n",
    "seal = rs.SealElement(n=0, kxx=stfx, kyy=stfy, cxx=1e3, cyy=0.8e3)\n",
    "seal"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.3 BallBearingElement Class <a id='section4.3'></a>\n",
    "\n",
    "This class will create a bearing element based on some geometric and constructive parameters of ball bearings. The main difference is that cross-coupling stiffness and damping are not modeled in this case.\n",
    "\n",
    "Let's see an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 0\n",
    "n_balls= 8\n",
    "d_balls = 0.03\n",
    "fs = 500.0\n",
    "alpha = np.pi / 6\n",
    "tag = \"ballbearing\"\n",
    "ballbearing = rs.BallBearingElement(\n",
    "    n=n,\n",
    "    n_balls=n_balls,\n",
    "    d_balls=d_balls,\n",
    "    fs=fs,\n",
    "    alpha=alpha,\n",
    "    tag=tag,\n",
    ")\n",
    "ballbearing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.4 RollerBearingElement Class <a id='section4.4'></a>\n",
    "\n",
    "This class will create a bearing element based on some geometric and constructive parameters of roller bearings. The main difference is that cross-coupling stiffness and damping are not modeled in this case.\n",
    "\n",
    "Let's see an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 0\n",
    "n_rollers= 8\n",
    "l_rollers = 0.03\n",
    "fs = 500.0\n",
    "alpha = np.pi / 6\n",
    "tag = \"rollerbearing\"\n",
    "rollerbearing = rs.RollerBearingElement(\n",
    "    n=n,\n",
    "    n_rollers=n_rollers,\n",
    "    l_rollers=l_rollers,\n",
    "    fs=fs,\n",
    "    alpha=alpha,\n",
    "    tag=tag\n",
    ")\n",
    "rollerbearing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.5 MagneticBearingElement Class <a id='section4.5'></a>\n",
    "\n",
    "This class creates a magnetic bearing element. You can input electromagnetic parameters and PID gains. ROSS converts it to stiffness and damping coefficients. To do ir, use the class `MagneticBearingElement()`\n",
    "\n",
    "See the following reference for the electromagnetic parameters g0, i0, ag, nw, alpha:\n",
    "Book: Magnetic Bearings. Theory, Design, and Application to Rotating Machinery\n",
    "Authors: Gerhard Schweitzer and Eric H. Maslen\n",
    "Page: 84-95\n",
    "\n",
    "From: \"Magnetic Bearings. Theory, Design, and Application to Rotating Machinery\"\n",
    "Authors: Gerhard Schweitzer and Eric H. Maslen\n",
    "Page: 354\n",
    "\n",
    "Let's see an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "n = 0\n",
    "g0 = 1e-3\n",
    "i0 = 1.0\n",
    "ag = 1e-4\n",
    "nw = 200\n",
    "alpha = 0.392\n",
    "kp_pid = 1.0\n",
    "kd_pid = 1.0\n",
    "k_amp = 1.0\n",
    "k_sense = 1.0\n",
    "tag = \"magneticbearing\"\n",
    "mbearing = rs.MagneticBearingElement(\n",
    "    n=n,g0=g0,i0=i0,ag=ag,nw=nw,alpha=alpha, kp_pid=kp_pid,kd_pid=kd_pid, k_amp=k_amp, k_sense=k_sense, tag=tag\n",
    ")\n",
    "mbearing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.6 Creating bearing elements via Excel <a id='section4.6'></a>\n",
    "\n",
    "There's an option for creating bearing elements via an Excel file. The classmethod `.from_table()` reads an Excel file created and converts it to a `BearingElement` instance. Differently from creating shaft or disk elements, this method creates only a single bearing element. To create a list of bearing elements, the user should open several spreadsheets in the Excel file and run a list comprehension loop appending each elemnet to the list.\n",
    "\n",
    "A header with the names of the columns is required. These names should match the names expected by the routine (usually the names of the parameters, but also similar ones). The program will read every row bellow the header until they end or it reaches a NaN, which means if the code reaches to an empty line, it stops iterating.\n",
    "\n",
    "```text\n",
    "n : int\n",
    "    The node in which the bearing will be located in the rotor.\n",
    "file: str\n",
    "    Path to the file containing the bearing parameters.\n",
    "sheet_name: int or str, optional\n",
    "    Position of the sheet in the file (starting from 0) or its name. If none is passed, it is\n",
    "    assumed to be the first sheet in the file.\n",
    "```\n",
    "\n",
    "An example of Excel content can be found at diretory *ross/tests/data/bearing_seal_si.xls*, spreadsheet \"XLUserKCM\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# single bearing element\n",
    "file_path = Path(\"bearing_seal_si.xls\")\n",
    "bearing = rs.BearingElement.from_table(n=0, file=file_path)\n",
    "bearing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As `.from_table()` creates only a single bearing, let's see an example how to create multiple elements without typing the same command line multiple times.\n",
    "\n",
    "- First, in the EXCEL file, create multiple spreadsheets. Each one must hold the bearing coefficients and frequency data.\n",
    "\n",
    "- Then, create a list holding the node numbers for each bearing (respecting the order of the spreadsheets from the EXCEL file).\n",
    "\n",
    "- Finally, create a loop which iterates over the the nodes list and the spreadsheet."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# list of bearing elements\n",
    "\n",
    "# nodes = list with the bearing elements nodes number\n",
    "# file_path = Path(\"bearing_seal_si.xls\")\n",
    "# bearings = [rs.BearingElement.from_table(n, file_path, sheet_name=i) for i, n in enumerate(nodes)]\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 5: PointMass Class <a id='section5'></a>\n",
    "\n",
    "The `PointMass` class creates a point mass element. This element can be used to link other elements in the analysis. The mass provided can be different on the x and y direction (e.g. different support inertia for x and y directions).\n",
    "\n",
    "`PointMass` also keeps the mass, stiffness, damping and gyroscopic matrices sizes consistence. When adding 2 bearing elements in series, it opens a new node with new degrees of freedom (DoF) (*see section 4.1.3*) and expands the stiffness and damping matrices. For this reason, it's necessary to add mass values to those DoF to match the matrices sizes.\n",
    "\n",
    "If you input the argument `m`, the code automatically replicate the mass value for both directions \"x\" and \"y\".\n",
    "\n",
    "Let's see an example of creating point masses:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# inputting m\n",
    "p0 = rs.PointMass(n=0, m=2)\n",
    "p0.M() # returns de mass matrices for the element"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# inputting mx and my\n",
    "p1 = rs.PointMass(n=0, mx=2, my=3)\n",
    "p1.M()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 6: Rotor Class <a id='section6'></a>\n",
    "\n",
    "`Rotor` is the main class from ROSS. It takes as argument lists with all elements and assembles the mass, gyroscopic, damping and stiffness global matrices for the system. The object created has several methods that can be used to evaluate the dynamics of the model (they all start with the prefix `.run_`).\n",
    "\n",
    "To use this class, you must input all the already instantiated elements in a list format.\n",
    "\n",
    "If the shaft elements are not numbered, the class set a number for each one, according to the element's position in the list supplied to the rotor constructor.\n",
    "\n",
    "To assemble the matrices, the `Rotor` class takes the local DoF's index from each element (element method `.dof_mapping()`) and calculate the global index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.1 Creating a rotor model <a id='section6.1'></a>\n",
    "\n",
    "Let's create a simple rotor model with $1.5 m$ length with 6 identical shaft elements, 2 disks, 2 bearings in the shaft ends and a support linked to the first bearing. First, we create the elements, then we input them to the `Rotor` class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "n = 6\n",
    "\n",
    "shaft_elem = [\n",
    "    rs.ShaftElement(\n",
    "        L=0.25,\n",
    "        idl=0.0,\n",
    "        odl=0.05,\n",
    "        material=steel,\n",
    "        shear_effects=True,\n",
    "        rotary_inertia=True,\n",
    "        gyroscopic=True,\n",
    "    )\n",
    "    for _ in range(n)\n",
    "]\n",
    "\n",
    "disk0 = rs.DiskElement.from_geometry(\n",
    "    n=2, material=steel, width=0.07, i_d=0.05, o_d=0.28\n",
    ")\n",
    "disk1 = rs.DiskElement.from_geometry(\n",
    "    n=4, material=steel, width=0.07, i_d=0.05, o_d=0.28\n",
    ")\n",
    "disks = [disk0, disk1]\n",
    "\n",
    "stfx = 1e6\n",
    "stfy = 0.8e6\n",
    "bearing0 = rs.BearingElement(0, kxx=stfx, kyy=stfy, cxx=0, n_link=7)\n",
    "bearing1 = rs.BearingElement(6, kxx=stfx, kyy=stfy, cxx=0)\n",
    "bearing2 = rs.BearingElement(7, kxx=stfx, kyy=stfy, cxx=0)\n",
    "\n",
    "bearings = [bearing0, bearing1, bearing2]\n",
    "\n",
    "pm0 = rs.PointMass(n=7, m=30)\n",
    "pointmass = [pm0]\n",
    "\n",
    "rotor1 = rs.Rotor(shaft_elem, disks, bearings, pointmass)\n",
    "\n",
    "print(\"Rotor total mass = \", np.round(rotor1.m, 2))\n",
    "print(\"Rotor center of gravity =\", np.round(rotor1.CG, 2))\n",
    "\n",
    "# plotting the rotor model\n",
    "rotor1.plot_rotor()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.2 Creating a rotor from sections <a id='section6.2'></a>\n",
    "\n",
    "An alternative to build rotor models is dividing the rotor in sections. Each section gets the same number of shaft elements.\n",
    "\n",
    "There's an important difference in this class method when placing disks and bearings. The argument `n` will refer, not to the element node, but to the section node. So if your model has 3 sections with 4 elements each, there're 4 section nodes and 13 element nodes.\n",
    "\n",
    "Let's repeat the rotor model from the last example, but using `.from_section()` class method, without the support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "i_d = 0\n",
    "o_d = 0.05\n",
    "\n",
    "# inner diameter of each section\n",
    "i_ds_data = [0, 0, 0]\n",
    "# outer diameter of each section\n",
    "o_ds_data = [0.05, 0.05, 0.05]\n",
    "# length of each section\n",
    "leng_data = [0.5, 0.5, 0.5]\n",
    "\n",
    "material_data = [steel, steel, steel]\n",
    "# material_data = steel\n",
    "stfx = 1e6\n",
    "stfy = 0.8e6\n",
    "\n",
    "# n = 0 refers to the section 0, first node\n",
    "bearing0 = rs.BearingElement(n=0, kxx=stfx, kyy=stfy, cxx=1e3)\n",
    "\n",
    "# n = 3 refers to the section 2, last node\n",
    "bearing1 = rs.BearingElement(n=3, kxx=stfx, kyy=stfy, cxx=1e3)\n",
    "bearings = [bearing0, bearing1]\n",
    "\n",
    "# n = 1 refers to the section 1, first node\n",
    "disk0 = rs.DiskElement.from_geometry(\n",
    "    n=1,\n",
    "    material=steel,\n",
    "    width=0.07,\n",
    "    i_d=0.05,\n",
    "    o_d=0.28\n",
    ")\n",
    "\n",
    "# n = 2 refers to the section 2, first node\n",
    "disk1 = rs.DiskElement.from_geometry(\n",
    "    n=2,\n",
    "    material=steel,\n",
    "    width=0.07,\n",
    "    i_d=0.05,\n",
    "    o_d=0.28\n",
    ")\n",
    "disks = [disk0,disk1]\n",
    "\n",
    "rotor2 = rs.Rotor.from_section(\n",
    "    brg_seal_data=bearings,\n",
    "    disk_data=disks,\n",
    "    idl_data=i_ds_data,\n",
    "    leng_data=leng_data,\n",
    "    odl_data=o_ds_data, \n",
    "    nel_r=4,\n",
    "    material_data=steel,\n",
    ")\n",
    "\n",
    "print(\"Rotor total mass = \", np.round(rotor2.m, 2))\n",
    "print(\"Rotor center of gravity =\", np.round(rotor2.CG, 2))\n",
    "rotor2.plot_rotor()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.3 Visualizing the rotor model  <a id='section6.3'></a>\n",
    "\n",
    "It is interesting to plot the rotor to check if the geometry checks with what you wanted to the model. Use the `.plot_rotor()` method to create a plot.\n",
    "\n",
    "`nodes` argument is useful when your model has lots of nodes and the visualization of nodes label may be confusing. Set an increment to the plot nodes label\n",
    "\n",
    "ROSS uses **PLOTLY** as main plotting library: \n",
    "\n",
    "With the Plotly, you can hover the mouse icon over the shaft, disk and point mass elements to check some of their parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "rotor1.plot_rotor()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's visualize another rotor example with **overlapping shaft elements**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "shaft_file = Path(\"shaft_si.xls\")\n",
    "shaft = rs.ShaftElement.from_table(\n",
    "    file=shaft_file, sheet_type=\"Model\", sheet_name=\"Model\"\n",
    ")\n",
    "\n",
    "file_path = Path(\"shaft_si.xls\")\n",
    "list_of_disks = rs.DiskElement.from_table(file=file_path, sheet_name=\"More\")\n",
    "\n",
    "bearing1 = rs.BearingElement.from_table(n=7, file=\"bearing_seal_si.xls\", scale_factor=0.5)\n",
    "bearing2 = rs.BearingElement.from_table(n=48, file=\"bearing_seal_si.xls\", scale_factor=0.5)\n",
    "\n",
    "bearings = [bearing1, bearing2]\n",
    "\n",
    "rotor3 = rs.Rotor(shaft, list_of_disks, bearings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "node_increment = 5\n",
    "rotor3.plot_rotor(nodes=node_increment)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.4 Saving a rotor model  <a id='section6.4'></a>\n",
    "\n",
    "You can save a rotor model using the method `.save()`. This method saves the each element type and the rotor object in different *.toml* files. \n",
    "\n",
    "You just need to input a name and the diretory, where it will be saved. If you don't input a file_path, the rotor model is saved inside the \"ross\" folder.\n",
    "\n",
    "To save the `rotor2` we can use:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rotor2.save('rotor2.toml')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6.5 Loading a rotor model  <a id='section6.5'></a>\n",
    "\n",
    "You can load a rotor model using the method `.load()`. This method loads a previously saved rotor model.\n",
    "\n",
    "You just need to input the file path to the method.\n",
    "\n",
    "Now, let's load the `rotor2` we saved before:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rotor2_1 = rs.Rotor.load('rotor2.toml')\n",
    "rotor2_1 == rotor2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Section 7: ROSS Units System <a id='section7'></a>\n",
    "\n",
    "ROSS uses an units system package called [Pint](https://pint.readthedocs.io/en/stable/).\n",
    "\n",
    "`Pint` defines, operates and manipulates **physical quantities**: the product of a numerical value and a unit of measurement. It allows arithmetic operations between them and conversions from and to different units.\n",
    "\n",
    "With `Pint`, it's possible to define units to every element type available in ROSS and manipulate the units when plotting graphs. ROSS takes the user-defined units and internally converts them to the International System (SI).\n",
    "\n",
    "**Important**: It's not possible to manipulate units for attributes from any class. Attributes' values are always returned converted to SI. **Only plot methods** are able to manipulate the output unit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.1 Inserting units <a id='section7.1'></a>\n",
    "\n",
    "Working with `Pint` requires a specific syntax to assign an unit to an argument.\n",
    "\n",
    "First of all, it's necessary to import a function called `Q_` from `ross.units`. This function must be assigned to every variable that are desired to have units, followed by a *tuple* containing the magnitude and the unit (in string format).\n",
    "\n",
    "The example below shows how to create a material using `Pint`, and how it is returned to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from ross.units import Q_\n",
    "\n",
    "rho = Q_(487.56237, \"lb/foot**3\")  # Imperial System\n",
    "E = Q_(211.e9, \"N/m**2\")           # International System\n",
    "G_s=Q_(81.2e9, \"N/m**2\")           # International System\n",
    "\n",
    "steel4 = rs.Material(name=\"steel\", rho=rho, E=E, G_s=G_s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note**: Taking a closer look to the output values, the material density is converted to the SI and it's returned this way to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(steel4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same syntax applies to elements instantiation, if units are desired. Besides, notice the output is displayed in SI units.\n",
    "\n",
    "#### Shaft Element using Pint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "L = Q_(10, \"in\")\n",
    "i_d = Q_(0., \"meter\")\n",
    "o_d = Q_(0.05, \"meter\")\n",
    "\n",
    "elem_pint = rs.ShaftElement(L=L, idl=i_d, odl=o_d, material=steel)\n",
    "print(elem_pint)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Bearing Element using Pint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "kxx = Q_(2.54e4, \"N/in\")\n",
    "cxx = Q_(1e2, \"N*s/m\")\n",
    "\n",
    "brg_pint = rs.BearingElement(n=0, kxx=kxx, cxx=cxx)\n",
    "print(brg_pint)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.2 Manipulating units for plotting <a id='section7.2'></a>\n",
    "\n",
    "The plot methods presents arguments to change the units for each axis. This kind of manipulation does not affect the resulting data stored. It only converts the data on the graphs.\n",
    "\n",
    "The arguments names follow a simple logic. It is the \"axis name\" underscore \"units\" (axisname_units). It should help the user to identify which axis to modify. For example:\n",
    "\n",
    "- frequency_units:\n",
    "    - \"rad/s\", \"RPM\", \"Hz\"...\n",
    "- amplitude_units:\n",
    "    - \"m\", \"mm\", \"in\", \"foot\"...\n",
    "- displacement_units:\n",
    "    - \"m\", \"mm\", \"in\", \"foot\"...\n",
    "- rotor_length_units:\n",
    "    - \"m\", \"mm\", \"in\", \"foot\"...\n",
    "- moment_units:\n",
    "    - \"N/m\", \"lbf/foot\"...\n",
    "\n",
    "It's not necessary to add units previously to each element or material to use `Pint` with plots. But keep in mind ROSS will considers results values in the SI units.\n",
    "\n",
    "**Note**: If you input data using the Imperial System, for example, without using Pint, ROSS will consider it's in SI if you try to manipulate the units when plotting.\n",
    "\n",
    "Let's run a simple example of manipulating units for plotting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "samples = 31\n",
    "speed_range = np.linspace(315, 1150, samples)\n",
    "\n",
    "campbell = rotor3.run_campbell(speed_range)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Plotting with default options will bring graphs with SI units. X and Y axes representing the frequencies are set to `rad/s` "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "campbell.plot()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's change the units to `RPM`.\n",
    "\n",
    "Just by adding `frequency_units=\"rpm\"` to plot method, you'll change the plot units."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "campbell.plot(frequency_units=\"RPM\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
